/*
 *  Axolotl - Alternate persistence for Grails
 *  Copyright (C) 2008  Juanjo García Latre
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
import com.googlecode.axolotl.cache.EntityCachingDatabaseInterceptor
import com.googlecode.axolotl.cache.QueryCachingDatabaseInterceptor
import com.googlecode.axolotl.jdbc.SpringJdbcDatabase
import com.googlecode.axolotl.jdbc.GrailsDomainSqlFactory
import com.googlecode.axolotl.metaclass.*
import com.googlecode.axolotl.query.groovy.QueryBuilder
import javax.sql.DataSource
import org.springframework.aop.framework.ProxyFactoryBean


class AxolotlGrailsPlugin {
    def version = 0.1
    def dependsOn = [:]
	
    static final def EMPTY_ARGS = [] as Object[]
	
    def doWithSpring = {
    	sqlFactory(GrailsDomainSqlFactory) {
    		grailsApplication = ref("grailsApplication", true)
    	}
        database(SpringJdbcDatabase) {
        	dataSource = ref("dataSource")
        	sqlFactory = ref("sqlFactory")
        }
        
        cacheManager(org.springframework.cache.ehcache.EhCacheManagerFactoryBean) {
        }
        entitiesCache(org.springframework.cache.ehcache.EhCacheFactoryBean) {
			cacheManager = ref("cacheManager")
        }
        cacheProviderFacade(com.googlecode.axolotl.cache.ehcache.NonSerializableEhCacheFacade) {
			cacheManager = ref("cacheManager")
		}        
        entityCacheInterceptor(EntityCachingDatabaseInterceptor) {
    		grailsApplication = ref("grailsApplication", true)
    		cacheProviderFacade = ref("cacheProviderFacade")
        }
        queryCacheInterceptor(QueryCachingDatabaseInterceptor) {
    		grailsApplication = ref("grailsApplication", true)
    		cacheProviderFacade = ref("cacheProviderFacade")
        }
        cachingDatabase(ProxyFactoryBean) {
        	target = ref("database")
        	interceptorNames = ["entityCacheInterceptor", "queryCacheInterceptor"]
        	frozen = true
        	exposeProxy = true
        }
    }
   
    def doWithApplicationContext = { applicationContext ->
        // TODO Implement post initialization spring config (optional)		
    }

    def doWithWebDescriptor = { xml ->
        // TODO Implement additions to web.xml (optional)
    }
	                                      
    
    def doWithDynamicMethods = { ctx ->
        def database = ctx.database
        
        def countMethod = new CountPersistentMethod(database, application)
        def listMethod = new ListPersistentMethod(database, application)
        def getMethod = new GetPersistentMethod(database, application)
        def insertMethod = new InsertPersistentMethod(database, application)
        def updateMethod = new UpdatePersistentMethod(database, application)
        def deleteMethod = new DeletePersistentMethod(database, application)
        
        for (dc in application.domainClasses) {
	        MetaClass mc = dc.metaClass
	        mc.'static'._count = {
	        	countMethod.invoke(delegate, '_count', EMPTY_ARGS)
	        }
	        mc.'static'._list = {
	        	listMethod.invoke(delegate, '_list', EMPTY_ARGS)
	        }
	        mc.'static'._get = { id ->
	        	getMethod.invoke(delegate, '_get', [id] as Object[])
	        }
	        mc.'static'._query = { closure ->
	        	def builder = new QueryBuilder(delegate)
	        	closure.delegate = builder
	        	closure()
	        	return builder.query
	        }
	        mc._insert = {
	        	insertMethod.invoke(delegate, '_insert', EMPTY_ARGS)
	        }
	        mc._update = {
	        	updateMethod.invoke(delegate, '_update', EMPTY_ARGS)
	        }
	        mc._delete = {
	        	deleteMethod.invoke(delegate, '_delete', EMPTY_ARGS)
	        }
        }
    }
    
	
    def onChange = { event ->
        // TODO Implement code that is executed when this class plugin class is changed  
        // the event contains: event.application and event.applicationContext objects
    }
                                                                                  
    def onApplicationChange = { event ->
        // TODO Implement code that is executed when any class in a GrailsApplication changes
        // the event contain: event.source, event.application and event.applicationContext objects
    }

}
